#pragma once

#include "cstd_lib.h"
#include "Type.h"
#include <Arduino.h>
#include <Wire.h>

class SimulationPCAL6416A
{
public:
    static std::unique_ptr<SimulationPCAL6416A> instance_uptr;

public:
    enum Register : uint8_t
    {
        REG_INPUT_PORT0 = 0x00,            // Port0 输入状态
        REG_INPUT_PORT1 = 0x01,            // Port1 输入状态
        REG_OUTPUT_PORT0 = 0x02,           // Port0 输出寄存器
        REG_OUTPUT_PORT1 = 0x03,           // Port1 输出寄存器
        REG_POLARITY_PORT0 = 0x04,         // Port0 极性反转
        REG_POLARITY_PORT1 = 0x05,         // Port1 极性反转
        REG_DIRECTION_CONFIG_PORT0 = 0x06, // Port0 方向配置
        REG_DIRECTION_CONFIG_PORT1 = 0x07, // Port1 方向配置
        REG_PULL_ENABLE_PORT0 = 0x46,      // Port0 上下拉使能
        REG_PULL_ENABLE_PORT1 = 0x47,      // Port1 上下拉使能
        REG_PULL_SELECT_PORT0 = 0x48,      // Port0 上下拉选择
        REG_PULL_SELECT_PORT1 = 0x49       // Port1 上下拉选择
    };
    static constexpr uint8_t ADDR_BASE = 0b0100000;
    static constexpr uint8_t ADDR_DEVICE_0 = ADDR_BASE | 0b000;
    static constexpr uint8_t ADDR_DEVICE_1 = ADDR_BASE | 0b001;
    static constexpr uint32_t BAUD = 400 * 1000;
    static constexpr size_t IO_MAX_COUNT = 16;

    ~SimulationPCAL6416A()
    {
        if (m_i2c_bus_handler_ptr)
            Deinit();
    }

    void Init(bool device_id,
              const std::array<uint8_t, 16> &gpio_map,
              TwoWire *i2c_bus_handler_ptr,
              uint8_t i2c_sda = 0xFF,
              uint8_t i2c_scl = 0xFF);

    void Deinit();

    void WriteAllGPIO();
    void ReadAllGPIOLevel();

private:
    TwoWire *m_i2c_bus_handler_ptr = nullptr;

    std::array<GPIOCode, IO_MAX_COUNT> m_gpio_map = {};

    uint8_t m_cache_input[2] = {0}; // 输入状态缓存

    uint8_t m_cache_dir_cfg[2] = {0xFF, 0xFF};     // 方向配置缓存（默认输入）
    uint8_t m_cache_polarity[2] = {0};             // 极性反转
    uint8_t m_cache_output[2] = {0xFF, 0xFF};      // 输出状态缓存（默认高电平）
    uint8_t m_cache_pull_enable[2] = {0};          // 上拉/下拉使能
    uint8_t m_cache_pull_select[2] = {0xFF, 0xFF}; // 上拉/下拉选择 (0下拉，1上拉)

    uint8_t m_from_master_command = 0xFF; // 来自主机的命令
    uint8_t m_rx_buffer[2] = {0xFF, 0xFF};

    void ReceiveCallback(int numBytes);
    void RequestCallback();
};
